{
 "cells": [
  {
   "cell_type": "raw",
   "metadata": {
    "vscode": {
     "languageId": "raw"
    }
   },
   "source": [
    ".. _nb_pinsga2:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    ".. meta::\n",
    "    :description:  An implementation of a progressivly interactive NSGA-II algorithm that uses user preference to guide the solving of multi-objective optimization problems. Value function optimization is used to take user generated ranking of key points to model user's preferences."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    ".. meta::\n",
    "    :keywords: PINSGA2, PINSGA-II, Progressivly Interactive, Multi-objective Optimization, Python"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# PI-NSGA-II: Progressivly Interactive NSGA-II"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "PI-NSGA-II (PINSGA2) is an implementation of PI-EMO-VF <cite data-cite=\"piemovf\"></cite> for the NSGA-II algorithm. It interactively employs user preferences to guide the multi-objective optimization process using a custom domination operator. \n",
    "\n",
    "At regular intervals ($\\tau$ generations), the algorithm pauses to select $\\eta$ well-spaced points from the current Pareto front. These points are presented to the decision-maker, who ranks them based on personal preference. The ranking can be either pairwise, where solutions are compared in pairs to establish a relative preference order, or absolute where solutions are ranked from highest to lowest preference. Ranking can either be made by a human decision maker via the command line, or automatically with a programmed automatic decision maker. \n",
    "\n",
    "The user-provided rankings are then used to fit a value function that models the decision maker's preferences. This value function modifies the domination of points for the next $\\tau$ generations, and then the process is repeated.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Example"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This is a simple example with a custom automatedDM. This example uses ZDT3, since its disconnected Pareto-optimal front illustrates how the algorithm guides the optimization through the objective space. The decision maker is set to rank four points ($\\eta$) every ten generations ($\\tau$) based on their distance from the center of the second region of the solution. In this case imagine the decision maker only wants one or more solutions in this area of the search space, and the other solutions are undisireable."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<pymoo.visualization.scatter.Scatter at 0x7f7814988850>"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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AF6BouwHAgICdPfdd0uSlixZoiNHjpicCAAAoPajKPuJrl27qlevXiopKdGbb77JQ0gAAAAug6LsR+666y7ZbDalpKTwaGsAAIDLoCj7kaZNm2rs2LGSpH/9618qKCgwOREAAEDtRVH2MzfffLOioqJ06tQpffLJJ2bHAQAAqLUoyn4mODhY//M//yNJ+vTTT3Xs2DGTEwEAANROFGU/1Lt3b8XHx6uoqEivv/46N/YBAAC4QVH2QxaLRffee68CAwO1ZcsWrV692uxIAAAAtQ5F2U81adJE48ePlyT9+9//5ol9AAAAv0JR9mNjx45VTEyMsrKyNH/+fLPjAAAA1CoUZT9ms9n0hz/8QZL0zTffaOfOnSYnAgAAqD0oyn6uU6dOGjlypCTplVdeUWFhocmJAAAAageKMnTnnXeqQYMGOnr0qD766COz4wAAANQKFGUoLCxM9913nyTH2sqpqakmJwIAADAfRRmSHGsr9+vXTyUlJXr55ZdVVFRkdiQAAABTUZThdO+996pu3bpKTU3Vp59+anYcAAAAU1GU4VSvXj3dc889kqSPPvpIhw4dMjkRAACAeSjKcDFo0CD16tVLRUVFmjNnDlMwAACA36Iow4XFYtH999+v8PBwpaam6uOPPzY7EgAAgCkoyrhI/fr1natgLFy4UPv37zc5EQAAQM2jKMOtfv36OVfBeOGFF5Sfn292JAAAgBpFUYZbFotFkydPdj6I5O233zY7EgAAQI2iKKNM4eHhmjJliiTpq6++0saNG01OBAAAUHMshmEYZoeorM8//1zPPfecQkJCFBAQoNdee02dO3d2O7ZDhw6Kjo522XbkyBE1bdpUq1evliTdcccd2rNnj0JCQpxjOnXqpNdee63cmbKzs2W325WVlaWIiIhKHFXt8+abb+qLL76Q3W7XK6+8ovr165sdCQAAoNLK29dsNZjJozZs2KCJEycqJSVFcXFxevfddzVy5Ejt3r1b4eHhF42Pjo5WUlKSy7Zx48Zp8ODBLts++ugjtWrVqhqTe5/bb79dW7duVVpaml588UU9/fTTslgsZscCAACoVl479WLWrFkaM2aM4uLiJEm33XabioqKNH/+fLfjfz3HNjMzU99++61uueWW6o7q9YKCgvTII48oODhYW7Zs0SeffGJ2JAAAgGrntUV5+fLl6tmzp/N9QECA4uPjtWzZMrfjW7du7fJ+wYIFGj16NNMIyikmJsa5ZNz777+vnTt3mpwIAACgenllUc7IyFB2drYaN27ssj06Orrcj12eP3++7rzzzou2z5w5U4MGDVK/fv10//336+TJk5fcT0FBgbKzs11evmrIkCEaPHiwDMPQ7NmzdfbsWbMjAQAAVBuvLMp5eXmSpODgYJftwcHBzs8uZdeuXTpx4oSGDx/usr1du3YaMGCAVqxYoZUrV6qgoEC9e/fWuXPnytzXzJkzZbfbna+YmJhKHJF3KF0yrnnz5srIyNDf//53HnENAAB8llcW5dDQUEmOq7m/VFBQ4PzsUubPn6/bb79dAQGuhz9jxgzdeuutCggIUGBgoF588UUdPnxYCxYsKHNf06dPV1ZWlvOVnp5eiSPyHiEhIXr00UdVp04d7dixg/WVAQCAz/LKohwZGSm73X7RtIgTJ04oNjb2kt8tLi7WBx984Hbaxa9FREQoKipKBw8eLHNMcHCwIiIiXF6+rnnz5nrooYckSV9++aVWrlxpciIAAADP88qiLDnmy6akpDjfG4ahTZs2adiwYZf83jfffKM2bdqobdu2F3324IMPurwvKChQRkaGWrRo4ZnQPqRXr14aP368JOmVV17R7t27TU4EAADgWV5blKdNm6YlS5bowIEDkqQPPvhAVqtVEydOlCT169dPjz766EXfK+smPkmaN2+ekpOTne+fffZZ1a9fXzfddFM1HIH3u+WWW9S7d29duHBBzzzzjI4dO2Z2JAAAAI/x2geOJCYmav78+Ro/frzq1KmjgIAALV261Pmwkby8vIvmMJ89e1bLly/XW2+95Xafzz//vKZMmSKbzaa8vDxFRUVp5cqVioqKqvbj8UYWi0VTp07V9OnTtX//fj355JOaPXu27Ha72dEAAACqzKsfYV0b+eIjrC/n7Nmzeuihh3Tq1Cm1a9dOzzzzTLluqgQAADBDefua1069QO1Rr149PfnkkwoPD9e+ffv01FNPKT8/3+xYAAAAVUJRhkfExMTo6aefVlhYmHbt2qVnnnlGhYWFZscCAACoNIoyPKZt27Z66qmnFBISom3btumpp55Sbm6u2bEAAAAqhaIMj2rfvr2efPJJZ1mePn26MjMzzY4FAABQYRRleFznzp01a9Ys1atXT4cOHdLDDz/s808sBAAAvoeijGrRpk0bzZ49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",
      "text/plain": [
       "<Figure size 800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from pymoo.algorithms.moo.pinsga2 import PINSGA2, AutomatedDM\n",
    "from pymoo.problems import get_problem\n",
    "from pymoo.optimize import minimize\n",
    "import numpy as np\n",
    "from pymoo.visualization.scatter import Scatter\n",
    "\n",
    "class simpleDM( AutomatedDM ):\n",
    "    \n",
    "    def makeDecision(self, F): \n",
    "        # Euclidean distance from center of region # 2\n",
    "        distances = np.sqrt( ( F[:, 0] - 0.21999557 )**2 + ( 0.40167775 - F[:, 1] )**2 )  \n",
    "\n",
    "        if distances[0] < distances[1]:\n",
    "            out = 'a'\n",
    "        elif distances[1] < distances[0]:\n",
    "            out = 'b'\n",
    "        else:\n",
    "            out = 'c'\n",
    "        \n",
    "        return out\n",
    "\n",
    "simple_dm = simpleDM()\n",
    "\n",
    "problem = get_problem(\"zdt3\")\n",
    "\n",
    "algorithm = PINSGA2(pop_size=30, \n",
    "                    tau=10,\n",
    "                    eta=4,\n",
    "                    opt_method=\"trust-constr\", \n",
    "                    vf_type=\"poly\",\n",
    "                    ranking_type=\"pairwise\",\n",
    "                    automated_dm=simple_dm)\n",
    "\n",
    "res = minimize(problem,\n",
    "               algorithm,\n",
    "               ('n_gen', 100),\n",
    "               seed=1,\n",
    "               verbose=False)\n",
    "\n",
    "plot = Scatter()\n",
    "plot.add(problem.pareto_front(), plot_type=\"line\", color=\"black\", alpha=0.7)\n",
    "plot.add(res.F, facecolor=\"none\", edgecolor=\"red\")\n",
    "plot.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Parameters"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "| Name                         | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           | Parameter        | Possible values                                                                                                                                                                                                                                                                                                                                                                                                               |\n",
    "| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n",
    "| $\\tau$                       | Number of generations between each stakeholder prompt                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 | `tau`            | $1 < \\tau$ (default of 10)                                                                                                                                                                                                                                                                                                                                                                                                    |\n",
    "| $\\eta$                       | Number of points for the decision maker to rank each $\\tau$ generations                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               | `eta`            | $2<\\eta$ (default of 4)                                                                                                                                                                                                                                                                                                                                                                                                       |\n",
    "| Optimization method          | The optimization method used to fit the value function                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                | `opt_method`     | <ul><br>  <li ><code class=\"docutils literal notranslate\"><span class=\"pre\">trust-constr</span></code> (default)</li><br>  <li ><code class=\"docutils literal notranslate\"><span class=\"pre\">SLSQP</span></code></li><br>  <li ><code class=\"docutils literal notranslate\"><span class=\"pre\">ES</span></code></li><br>  <li ><code class=\"docutils literal notranslate\"><span class=\"pre\">GA</span></code> </li><br></ul><br> |\n",
    "| Value function type          | The mathematical format of your value function                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        | `vf_type`        | <ul><br>  <li ><code class=\"docutils literal notranslate\"><span class=\"pre\">poly</span></code> (default) A polynomial representation of user preference</li><br>  <li ><code class=\"docutils literal notranslate\"><span class=\"pre\">linear</span></code> A linear representation of user preference</li><br></ul><br>                                                                                                         |\n",
    "| Ranking method               | The method of eliciting preference from the stakeholder                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               | `ranking_method` | <ul><br>  <li ><code class=\"docutils literal notranslate\"><span class=\"pre\">pairwise</span></code> (default) User only compares two solutions at a time</li><br>  <li ><code class=\"docutils literal notranslate\"><span class=\"pre\">absolute</span></code> - User must provide a complete ranking</li><br></ul>                                                                                                               |\n",
    "| Automated ranking (optional) | Whether to have a real human rank solutions or an automated decision maker. <ul><br><li>If<code class=\"docutils literal notranslate\"><span class=\"pre\">ranking_method == 'absolute'</span></code>, makeDecision will recieve eta pts and must return a complete ranking of those points</li><br><li>If <code class=\"docutils literal notranslate\"><span class=\"pre\">ranking_method == 'pairwise'</span></code>, makeDecision will recieve pairs of points and must return 'a' if the first point is better, 'b' if the second point is better, or 'c' if they are equally good.</li><br></ul><br><br> | `automated_dm`   | A class that implements the `automatedDM` class.                                                                                                                                                                                                                                                                                                                                                                              |\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### API"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {
    "vscode": {
     "languageId": "raw"
    }
   },
   "source": [
    ".. autoclass:: pymoo.algorithms.moo.pinsga2.PINSGA2\n",
    "    :noindex:"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.18"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
